#ifndef __BASIC_LOG_H__
#define __BASIC_LOG_H__

#include <cstdio>
#include <iostream>

#include "basic/types.h"
#include "basic/platform.h"

namespace basic {
enum class ELogLevel : unsigned short {
  E_LOG_TRACE = 0,
  E_LOG_DEBUG,
  E_LOG_INFO,
  E_LOG_WARN,
  E_LOG_ERROR,
  E_LOG_FATAL,
};

M_API_FUNC extern void gLogSetLevel(ELogLevel lvl);
M_API_FUNC extern void gLogSetFormat(const char* fmt);

M_API_FUNC extern void gLogDebug(const char* args...);
M_API_FUNC extern void gLogInfo(const char* args...);
M_API_FUNC extern void gLogWarn(const char* args...);
M_API_FUNC extern void gLogError(const char* args...);
M_API_FUNC extern void gLogFatal(const char* args...);

#ifdef DK_PLATFORM_OS_WINDOWS
extern HANDLE g_consoleHandle;
extern WORD g_oldColorAttrs;
extern WORD g_oldConsoleBackgroundColor;
#define _M_CONSOLE_COLOR_INFO FOREGROUND_GREEN | g_oldConsoleBackgroundColor
#define _M_CONSOLE_COLOR_DEBUG FOREGROUND_INTENSITY | FOREGROUND_BLUE | g_oldConsoleBackgroundColor
#define _M_CONSOLE_COLOR_WARN FOREGROUND_RED | FOREGROUND_GREEN | g_oldConsoleBackgroundColor
#define _M_CONSOLE_COLOR_ERROR FOREGROUND_RED | g_oldConsoleBackgroundColor
#define _M_CONSOLE_COLOR_FATAL                                                                                         \
  FOREGROUND_INTENSITY | FOREGROUND_RED | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE

#define _M_SET_CONSOLE_COLOR_TO(color)
//#define _M_SET_CONSOLE_COLOR_TO(color) SetConsoleTextAttribute(g_consoleHandle, color);

#else
// the following are UBUNTU/LINUX ONLY terminal color codes.
#define RESET "\033[0m"
#define BLACK "\033[30m"              /* Black */
#define RED "\033[31m"                /* Red */
#define GREEN "\033[32m"              /* Green */
#define YELLOW "\033[33m"             /* Yellow */
#define BLUE "\033[34m"               /* Blue */
#define MAGENTA "\033[35m"            /* Magenta */
#define CYAN "\033[36m"               /* Cyan */
#define WHITE "\033[37m"              /* White */
#define BOLDBLACK "\033[1m\033[30m"   /* Bold Black */
#define BOLDRED "\033[1m\033[31m"     /* Bold Red */
#define BOLDGREEN "\033[1m\033[32m"   /* Bold Green */
#define BOLDYELLOW "\033[1m\033[33m"  /* Bold Yellow */
#define BOLDBLUE "\033[1m\033[34m"    /* Bold Blue */
#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */
#define BOLDCYAN "\033[1m\033[36m"    /* Bold Cyan */
#define BOLDWHITE "\033[1m\033[37m"   /* Bold White */

#define g_oldColorAttrs RESET;
#define _M_CONSOLE_COLOR_INFO GREEN
#define _M_CONSOLE_COLOR_DEBUG BLUE
#define _M_CONSOLE_COLOR_WARN YELLOW
#define _M_CONSOLE_COLOR_ERROR RED
#define _M_CONSOLE_COLOR_FATAL BOLDRED

#define _M_SET_CONSOLE_COLOR_TO(color) std::cout << color;
#endif

inline void tpl_Print() {
}

template <typename T>
inline void tpl_Print(T v) {
  std::cout << v;
}

template <typename T, typename... Args>
void tpl_Print(T v, Args... args) {
  tpl_Print(v);
  tpl_Print(args...);
}
} // namespace basic

//#define M_LOG_SET_LEVEL(lvl) gLogSetLevel(lvl);
//#define M_LOG_SET_FORMAT(fmt) gLogSetFormat(fmt);

#define M_LOG_DEBUG(...)                                                                                               \
  _M_SET_CONSOLE_COLOR_TO(_M_CONSOLE_COLOR_DEBUG)                                                                      \
  basic::tpl_Print("[DEBUG]->", __FILE__, ",line:", __LINE__, ",", __VA_ARGS__, "\n");                                        \
  _M_SET_CONSOLE_COLOR_TO(g_oldColorAttrs)

#define M_LOG_INFO(...)                                                                                                \
  _M_SET_CONSOLE_COLOR_TO(_M_CONSOLE_COLOR_INFO)                                                                       \
  basic::tpl_Print("[ INFO]->", __FILE__, ",line:", __LINE__, ",", __VA_ARGS__, "\n");                                        \
  _M_SET_CONSOLE_COLOR_TO(g_oldColorAttrs)

#define M_LOG_WARN(...)                                                                                                \
  _M_SET_CONSOLE_COLOR_TO(_M_CONSOLE_COLOR_WARN)                                                                       \
  basic::tpl_Print("[ WARN]->", __FILE__, ",line:", __LINE__, ",", __VA_ARGS__, "\n");                                        \
  _M_SET_CONSOLE_COLOR_TO(g_oldColorAttrs)

#define M_LOG_ERROR(...)                                                                                               \
  _M_SET_CONSOLE_COLOR_TO(_M_CONSOLE_COLOR_ERROR)                                                                      \
  basic::tpl_Print("[ERROR]->", __FILE__, ",line:", __LINE__, ",", __VA_ARGS__, "\n");                                        \
  _M_SET_CONSOLE_COLOR_TO(g_oldColorAttrs)

#define M_LOG_FATAL(...)                                                                                               \
  _M_SET_CONSOLE_COLOR_TO(_M_CONSOLE_COLOR_FATAL)                                                                      \
  basic::tpl_Print("[FATAL]->", __FILE__, ",line:", __LINE__, ",", __VA_ARGS__, "\n");                                        \
  _M_SET_CONSOLE_COLOR_TO(g_oldColorAttrs)

#define M_LOG_DEBUG_IF(c, ...)                                                                                         \
  if (c) {                                                                                                             \
    M_LOG_DEBUG(__VA_ARGS__);                                                                                          \
  }

#define M_LOG_INFO_IF(c, ...)                                                                                          \
  if (c) {                                                                                                             \
    M_LOG_INFO(__VA_ARGS__);                                                                                           \
  }

#define M_LOG_WARN_IF(c, ...)                                                                                          \
  if (c) {                                                                                                             \
    M_LOG_WARN(__VA_ARGS__);                                                                                           \
  }

#define M_LOG_ERROR_IF(c, ...)                                                                                         \
  if (c) {                                                                                                             \
    M_LOG_ERROR(__VA_ARGS__);                                                                                          \
  }

#define M_LOG_FATAL_IF(c, ...)                                                                                         \
  if (c) {                                                                                                             \
    M_LOG_FATAL(__VA_ARGS__);                                                                                          \
  }

#endif
